package com.koi.system.controller.online;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.koi.common.anno.KoiLog;
import com.koi.common.constants.SaTokenSessionConstants;
import com.koi.common.enums.log.OperType;
import com.koi.common.satoken.KoiLoginUser;
import com.koi.model.vo.system.SysUserOnlineVo;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ･ᴗ･
 * @description [SysUserOnline]
 */
@RequiredArgsConstructor
@RestController
@RequestMapping("/koi")
public class SysUserOnlineController {

    /**
     * @description [在线用户]
     * @author ･ᴗ･
     */
    @SaCheckPermission("monitor:online:list")
    @GetMapping("/sysUserOnline/listPage")
    public Map<String, Object> listPage(SysUserOnlineVo vo) {
        // keyword: 查询关键字，只有包括这个字符串的 token 值才会被查询出来。
        // start: 数据开始处索引。
        // size: 要获取的数据条数 （值为-1代表一直获取到末尾）。
        // sortType: 排序方式（true=正序：先登录的在前，false=反序：后登录的在前）。
        // 获取所有已登录的会话id[这里是最合适的方法]，结果集从第 0 条开始，返回 10 条
        // 官网文档：https://sa-token.cc/doc.html#/up/search-session
        // 获取所有未过期的 sessionId[即用户ID]
        List<String> sessionKeyList = StpUtil.searchSessionId("", 0, -1, false);
        List<KoiLoginUser> loginUserList = null;
        Map<String, Object> map = null;
        if (CollectionUtil.isNotEmpty(sessionKeyList)) {
            loginUserList = new ArrayList<>();
            for (String sessionKey : sessionKeyList) {
                // 用户ID
                // String sessionId = StringUtils.substringAfterLast(sessionKey, ":");
                // 使用sessionKey获取SaSession对象
                SaSession saSession = StpUtil.getSessionBySessionId(sessionKey);
                // 获取登录用户对象
                KoiLoginUser koiLoginUser = saSession.getModel(SaTokenSessionConstants.LOGIN_USER, KoiLoginUser.class);
                // 添加到在线用户列表
                loginUserList.add(koiLoginUser);
            }
            map = new HashMap<>();
            Integer pageNo = vo.getPageNo();
            Integer pageSize = vo.getPageSize();
            String loginName = vo.getLoginName();
            String userName = vo.getUserName();
            String ipAddress = vo.getIpAddress();
            // 计算起始索引和结束索引
            int startIndex = (pageNo - 1) * pageSize;
            int endIndex = Math.min(startIndex + pageSize, loginUserList.size());
            // 如果有条件，先进行过滤，在进行分页显示
            List<KoiLoginUser> filteredList = loginUserList.stream()
                    .filter(loginUser -> (userName.isEmpty() || loginUser.getUserName().contains(userName)))
                    .filter(loginUser -> (loginName.isEmpty() || loginUser.getLoginName().contains(loginName)))
                    .filter(loginUser -> (ipAddress.isEmpty() || loginUser.getIpAddress().contains(ipAddress)))
                    .collect(Collectors.toList());
            // 使用Stream对列表进行分页操作
            List<KoiLoginUser> records = filteredList.stream()
                    .skip(startIndex)  // 跳过前startIndex条记录
                    .limit(endIndex - startIndex)  // 取出当前页的记录
                    .collect(Collectors.toList());  // 将结果转换为List
            map.put("total", loginUserList.size()); // 总条数
            map.put("current", vo.getPageNo()); // 当前第几页
            map.put("size", vo.getPageSize()); // 每页显示多少条
            map.put("pages", (int) Math.ceil((double) loginUserList.size() / pageSize)); // 总共多少页
            map.put("records", records); // 当前页的记录
            return map;
        } else {
            return Collections.emptyMap();
        }
    }

    /**
     * @description [踢人下线不会清除Token信息，而是将其打上特定标记，再次访问会提示：Token已被踢下线。]
     * @author ･ᴗ･
     */
    @SaCheckPermission("monitor:online:kickout")
    @GetMapping("/sysUserOnline/kickOut/{userId}")
    public void kickOut(@PathVariable("userId") Long userId) {
        // 将指定账号踢下线
        StpUtil.kickout(userId);
    }

    /**
     * @description [踢人下线不会清除Token信息，而是将其打上特定标记，再次访问会提示：Token已被踢下线。]
     * @author ･ᴗ･
     */
    @SaCheckPermission("monitor:online:logout")
    @GetMapping("/sysUserOnline/logout/{userId}")
    public void logout(@PathVariable("userId") Long userId) {
        // 强制指定账号注销下线
        StpUtil.logout(userId);
    }

}
